<html>
<head>
<link rel="shortcut icon" href="./favicon.ico">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="canonical" href="./Quadrature_Decoder.html">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Decodes a pair of digital signals in quadrature (offset by 90deg), which express the direction of motion. Outputs three sets of pulses: `step`, `direction`, and `error`. The signals are valid when `step` is high, unless `error` is also high.">
<title>Quadrature Decoder</title>
</head>
<body>

<p class="inline bordered"><b><a href="./Quadrature_Decoder.v">Source</a></b></p>
<p class="inline bordered"><b><a href="./legal.html">License</a></b></p>
<p class="inline bordered"><b><a href="./index.html">Index</a></b></p>

<h1>Quadrature Decoder</h1>
<p>Decodes a pair of digital signals in quadrature (offset by 90deg), which
 express the direction of motion. Outputs three sets of pulses: <code>step</code>,
 <code>direction</code>, and <code>error</code>. The signals are valid when <code>step</code> is high, unless
 <code>error</code> is also high.</p>
<p>This is an alternate implementation which does not use an FSM, but instead
 detects edges and distinguishes direction based on the state of the other
 signals at the time.</p>
<p>The <code>clock</code> may run at any speed, so long as it can properly sample <code>A</code> and
 <code>B</code>, thus the minimum clock speed is 2x the input toggle rate.</p>
<h2>Theory of Operation</h2>
<p>A quadrature waveform has two signals, A and B, one always 90deg out of
 phase with the other. If A leads B, when the edges of A arrive before the
 edges of B, then we define that as forward motion. If B leads A, we define
 that as backwards motion. Each edge indicates a step has been taken. If
 there are no edges, there is no motion. <em>Edges </em><em>never</em><em> happen at the same
 time, which is an error condition.</em></p>
<p>If A and B are external signals, they <strong>must</strong> first be synchronized to the
 clock and debounced, if generated mechanically, using
 a <a href="./Debouncer_Low_Latency.html">Debouncer</a>. Any two edges must arrive at
 least one clock cycle apart, else they happen at the same time, which is
 an error condition.</p>
<p>The following waveform diagram shows 6 steps of forward motion, followed by
 a brief pause and 7 steps of backwards motion. Each of the edges in a cycle
 of A and B, in each direction, are labelled in sequence, starting with the
 first rising edge of each cycle in each direction.</p>
<pre><code>    F0  F2              B1  B3
     ---     -------     ---
 A _|   |___|       |___|   |___

      F1  F3          B0  B2
       ---     ---     ---     ---
 B ___|   |___|   |___|   |___|
</code></pre>
<h2>Stuck-At Faults</h2>
<p>Note that there is one error condition that cannot be detected directly: if
 either the A or B input gets stuck, the other input will keep toggling and
 generate alternating forward and backward edges. This cannot be
 disinguished from a change in direction, as shown in the middle of the
 diagram, where A is constant while B still has edges.</p>
<p>Detecting this kind of error must be done at a higher level, such as
 sending a command to move in a certain direction, but not seeing that
 motion on the quadrature inputs, either because the device is physically
 stuck and no edges are seen, or only one set of edges are seen, decoding as
 minimal back-and-forth movement. The frequency of the edges would report
 the speed of motion at least, confirming that motion is happening and that
 a quadrature signal is stuck.</p>
<h2>Ports, Parameters, and Constants</h2>

<pre>
`default_nettype none

module <a href="./Quadrature_Decoder.html">Quadrature_Decoder</a>
(
    input   wire    clock,
    input   wire    A,          // Leading A means forward
    input   wire    B,          // Leading B means backwards
    output  reg     step,       // Outputs valid while high
    output  reg     direction,  // 0/1 -> backwards/forwards
    output  reg     error       // Overrides step
);
</pre>

<p>There are 4 possible forward or backward edges within the combined
 cycles of A and B.</p>

<pre>
    localparam EDGE_COUNT = 4;
    localparam EDGES_ZERO = {EDGE_COUNT{1'b0}};

    initial begin
        step        = 1'b0;
        direction   = 1'b0;
        error       = 1'b0;
    end
</pre>

<h3>Positive and Negative Edges on A</h3>

<pre>
    wire A_posedge;
    wire A_negedge;

    <a href="./Pulse_Generator.html">Pulse_Generator</a>
    A_rising
    (
        .clock              (clock),
        .level_in           (A),
        .pulse_posedge_out  (A_posedge),
        // verilator lint_off PINCONNECTEMPTY
        .pulse_negedge_out  (),
        .pulse_anyedge_out  ()
        // verilator lint_off PINCONNECTEMPTY
    );

    <a href="./Pulse_Generator.html">Pulse_Generator</a>
    A_falling
    (
        .clock              (clock),
        .level_in           (A),
        .pulse_negedge_out  (A_negedge),
        // verilator lint_off PINCONNECTEMPTY
        .pulse_posedge_out  (),
        .pulse_anyedge_out  ()
        // verilator lint_off PINCONNECTEMPTY
    );
</pre>

<h2>Positive and Negative Edges on B</h2>

<pre>
    wire B_posedge;
    wire B_negedge;

    <a href="./Pulse_Generator.html">Pulse_Generator</a>
    B_rising
    (
        .clock              (clock),
        .level_in           (A),
        .pulse_posedge_out  (B_posedge),
        // verilator lint_off PINCONNECTEMPTY
        .pulse_negedge_out  (),
        .pulse_anyedge_out  ()
        // verilator lint_off PINCONNECTEMPTY
    );

    <a href="./Pulse_Generator.html">Pulse_Generator</a>
    B_falling
    (
        .clock              (clock),
        .level_in           (A),
        .pulse_negedge_out  (B_negedge),
        // verilator lint_off PINCONNECTEMPTY
        .pulse_posedge_out  (),
        .pulse_anyedge_out  ()
        // verilator lint_off PINCONNECTEMPTY
    );
</pre>

<h2>Forward and Backward Edges</h2>
<p>There are 4 possible forward and backward edges, defined by when one
 signal has a positive or negative edge, and the state of the other
 signal at that time.  See the waveform diagram above for reference.</p>

<pre>
    reg [EDGE_COUNT-1:0] forward_edges  = EDGES_ZERO;
    reg [EDGE_COUNT-1:0] backward_edges = EDGES_ZERO;

    always @(*) begin
        forward_edges[0]  = (A_posedge == 1'b1) && (B == 1'b0);
        forward_edges[1]  = (B_posedge == 1'b1) && (A == 1'b1);
        forward_edges[2]  = (A_negedge == 1'b1) && (B == 1'b1);
        forward_edges[3]  = (B_negedge == 1'b1) && (A == 1'b0);

        backward_edges[0] = (B_posedge == 1'b1) && (A == 1'b0);
        backward_edges[1] = (A_posedge == 1'b1) && (B == 1'b1);
        backward_edges[2] = (B_negedge == 1'b1) && (A == 1'b1);
        backward_edges[3] = (A_negedge == 1'b1) && (B == 1'b0);
    end
</pre>

<h2>Forward and Backward Steps</h2>
<p>Let's reduce the edges from each direction to one signal which tells us
 if a step has been taken either forward or backward. Since no two edges
 can happen at the same time during normal operation, these two signals
 can never be active at the same time, which is a very useful
 property...</p>

<pre>
    reg forward_step  = 1'b0;
    reg backward_step = 1'b0;

    always @(*) begin
        forward_step  = (forward_edges  != EDGES_ZERO);
        backward_step = (backward_edges != EDGES_ZERO);
    end
</pre>

<h2>Decoded Outputs</h2>
<p>The <code>step</code> signal goes high whenever there is a forward or backward step.</p>
<p>The <code>direction</code> signal is high for a forward step, and low for
 a backward step, so we only need to use the forward step since they are
 mutually exclusive, as shown above.</p>
<p>Finally, if both a forward and backward step happen at the same time,
 this is impossible, and <code>error</code> is pulsed high. The direction output
 will signal forward, but it is unknown if it is correct. The step
 output will signal a step, but it is unknown if one or more steps have
 occured.</p>

<pre>
    always @(*) begin
        step        = (forward_step == 1'b1) || (backward_step == 1'b1);
        direction   = (forward_step == 1'b1);
        error       = (forward_step == 1'b1) && (backward_step == 1'b1);
    end

endmodule
</pre>

<hr>
<p><a href="./index.html">Back to FPGA Design Elements</a>
<center><a href="https://fpgacpu.ca/">fpgacpu.ca</a></center>
</body>
</html>

